package gttp.nspa.shared.om;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.user.client.rpc.IsSerializable;
import com.googlecode.objectify.Ref;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Load;
import com.googlecode.objectify.condition.IfNotNull;

@Entity(name = "BILL")
public class Billing implements IsSerializable {

    public static enum Status {
        /**
         * Khách đặt chỗ (qua điện thoại hoặc email)
         */
        BOOKING,

        /**
         * Khách hủy đặt chỗ (chỉ hỗ trợ khi chưa OPEN bill)
         */
        CANCELLED,

        /**
         * Khách tới và bắt đầu sử dụng dịch vụ
         */
        OPENING,

        /**
         * Khách đã thanh toán bill
         */
        PAID;
    }

    @Id
    private Long              id;

    /**
     * Thời gian mở bill
     */
    @Index
    private long              time   = System.currentTimeMillis();

    /**
     * Thời gian kết thúc bill
     */
    private Long              timeEnd;

    /**
     * Code của cashier trực
     */
    private String            cashier;

    /**
     * Tên thường gọi của khách hàng
     */
    @Index
    private String            alias;

    /**
     * Khách hàng có bill
     */
    @Load
    @Index(IfNotNull.class)
    private Ref<Customer>     customer;

    /**
     * Code của locker cấp cho khách hàng sử dụng
     */
    @Index(IfNotNull.class)
    private String            locker;

    /**
     * Danh sách các order mà khách hàng sử dụng
     */
    @Load
    @Index
    private List<Ref<IOrder>> orders;

    /**
     * Trạng thái của bill
     */
    @Index
    private Status            status = Status.BOOKING;

    @Index(IfNotNull.class)
    private String            reason;

    public Billing(String cashier, Customer customer) {
        this.cashier = cashier;
        setCustomer(customer);
    }

    public Billing(String cashier, String alias) {
        this.cashier = cashier;
        this.alias = alias;
    }

    public Long getId() {
        return id;
    }

    public long getTime() {
        return time;
    }

    public Long getTimeEnd() {
        return timeEnd;
    }

    public String getCashier() {
        return cashier;
    }

    public String getAlias() {
        return alias != null ? alias : (customer != null ? customer.get().getName() : null);
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public Customer getCustomer() {
        return customer != null ? customer.get() : null;
    }

    public void setCustomer(Customer customer) {
        if (customer != null)
            this.customer = Ref.create(customer);
        else
            this.customer = null;
    }

    public String getLocker() {
        return locker;
    }

    public Status getStatus() {
        return status;
    }

    public String getReason() {
        return reason;
    }

    public List<IOrder> getOrders() {
        List<IOrder> ords = null;
        if (orders != null) {
            ords = new ArrayList<>(orders.size());
            for (Ref<IOrder> order : orders) {
                ords.add(order.get());
            }
        }
        return ords;
    }

    /**
     * Thêm order vào bill. Khi trạng thái là PAID thì không thể thêm order
     * @param order
     * @return TRUE nếu thực hiện thành công
     */
    public boolean addOrder(IOrder order) {
        if (status != Status.PAID) {
            if (orders == null) {
                orders = new ArrayList<>();
            }
            orders.add(Ref.create(order));
            return true;
        }
        return false;
    }

    /**
     * Hủy order. Khi trạng thái là PAID thì không thể hủy order
     * @param orderId
     * @param reason
     *        Lý do hủy order
     * @return order đã được hủy
     */
    public IOrder cancelOrder(long orderId, String reason) {
        if (status != Status.PAID) {
            for (Ref<IOrder> order : orders) {
                if (order.key().getId() == orderId) {
                    IOrder ord = order.get();
                    if (ord != null && ord.cancel(reason))
                        return ord;
                }
            }
        }
        return null;
    }

    /**
     * Phục vụ order. Khi trạng thái là PAID thì không thể phục vụ order
     * @param orderId
     * @return order được phục vụ
     */
    public IOrder serveOrder(long orderId) {
        if (status != Status.PAID) {
            for (Ref<IOrder> order : orders) {
                if (order.key().getId() == orderId) {
                    IOrder ord = order.get();
                    if (ord != null && ord.serve())
                        return ord;
                }
            }
        }
        return null;
    }

    /**
     * Phục vụ order xong. Khi trạng thái là PAID thì không thể phục vụ order
     * @param orderId
     * @return order được phục vụ xong
     */
    public IOrder serveOrderDone(long orderId) {
        if (status != Status.PAID) {
            for (Ref<IOrder> order : orders) {
                if (order.key().getId() == orderId) {
                    IOrder ord = order.get();
                    if (ord != null && ord.serveDone())
                        return ord;
                }
            }
        }
        return null;
    }

    /**
     * Bill được mở (khách hàng đã tới và chuẩn bị sử dụng dịch vụ). Chỉ bill nào đang BOOKING mới open được<br>
     * Sau khi thực hiện thành công, bill chuyển qua trạng thái OPENING
     * @param locker
     *        Locker được cấp cho khách
     * @return TRUE nếu thực hiện thành công
     */
    public boolean open(String locker) {
        if (status == Status.BOOKING) {
            this.locker = locker;
            status = Status.OPENING;

            time = System.currentTimeMillis();
            if (customer != null)
                customer.get().updateLastVisit();
            return true;
        }
        return false;
    }

    /**
     * Khách hàng hủy bill, các order cũng bị hủy theo. Chỉ bill nào đang BOOKING mới hủy được<br>
     * Sau khi thực hiện thành công, bill chuyển về trạng thái CANCELLED
     * @param reason
     *        Lý do hủy bill
     * @return TRUE nếu thực hiện thành công
     */
    public boolean cancel(String reason) {
        if (status == Status.BOOKING) {
            status = Status.CANCELLED;
            this.reason = reason;
            List<IOrder> ords = getOrders();
            if (ords != null) {
                for (IOrder ord : ords) {
                    ord.cancel(null);
                }
            }
            timeEnd = System.currentTimeMillis();
            return true;
        }
        return false;
    }

    /**
     * Khách hàng đã thanh toán xong bill. Chỉ bill nào đang OPENING mới paid được<br>
     * Sau khi thực hiện thành công, bill chuyển về trạng thái PAID
     * @return TRUE nếu thực hiện thành công
     */
    public boolean paid() {
        if (status == Status.OPENING) {
            status = Status.PAID;
            timeEnd = System.currentTimeMillis();
            return true;
        }
        return false;
    }
}
